﻿
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace CharmL {
    public class Eclat {
        //ublic Paramenters Param  = 0;
        public bool IS_INCRE { get; set; }
        public int INCRE_SIZE { get; set; }
        public int INCRE_BATCH_SIZE { get; set; }
        public int READ_NUM_TRANS { get; set; }
        public double MINSUP_PER { get; set; }
        public int MINSUPPORT { get; set; }
        public double MINCONF { get; set; }
        public int DBASE_MAXITEM { get; set; }
        public int DBASE_NUM_TRANS { get; set; }
        public Constraints constraints { get; set; }
        public Dbase_Ctrl_Blk DCB { get; set; }
        public LatticeNode lattice { get; set; }
        public MaximalTest maxtest; //for maximality & closed (with cmax) testing
        public HashTable hashtest;
        public StreamWriter writer;
        public Eclat() {
            maxtest = new MaximalTest();
            hashtest = new HashTable();
            MINSUP_PER = 0;
            MINSUPPORT = -1;
            MINCONF = 0;
            DBASE_MAXITEM = 0;
            DBASE_NUM_TRANS = 0;
            lattice = new LatticeNode();
        }

        private void IncreLattice(Dictionary<int, long> dic) {
         //   List<int> time = new List<int>();
            if (IS_INCRE) {
                IndexTable table = new IndexTable();
                BuildIndexTable(table, lattice, DCB.NumF1);
                CLICL clicl = new CLICL();
                clicl.FirstNodes = table;
                //using (StreamWriter file = new System.IO.StreamWriter("Results.txt")) {
                //using (StreamReader sr = new StreamReader("incre.txt")) {
                String line;
                int totalIndex = READ_NUM_TRANS + 1;
                int increTotalSize = INCRE_SIZE;


                while (increTotalSize < INCRE_BATCH_SIZE) {
                    Console.WriteLine("INCREMENTAL");
                    int index = 1;
                    DateTime start = DateTime.Now;
                    using (MyStopWatch sw = new MyStopWatch()) {
                        while (index <= INCRE_SIZE && (line = DCB.reader.ReadLine()) != null) {
                            char[] delimiters = new char[] { ' ' };
                            string[] parts = line.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
                            List<int> itemSet = new List<int>();
                            foreach (var item in parts) {
                                int value = int.Parse(item);
                                if (value < DCB.FreqMap.Count && DCB.FreqMap[value] != -1) {
                                    itemSet.Add(DCB.FreqMap[value]);
                                }
                                //if (!DCB.FreqIdx.Contains(value)) {
                                //    continue;
                                //}
                            }
                            if (itemSet.Count == 0) {
                                // Console.WriteLine("Skip!!");
                                continue;
                            }
                            itemSet.Sort();
                            // Console.Write(" " + totalIndex);
                            int latticeSize = maxtest.lattice.Count;
                            LatticeNode temp = clicl.AddTransaction(1, itemSet, maxtest);
                            for (int i = maxtest.lattice.Count - 1; i >= latticeSize; i--) {
                                LatticeNode node = maxtest.lattice[i];
                                if (node.Sup < MINSUPPORT) {
                                    for (int j = node.Parent.Count - 1; j >= 0; j--) {
                                        node.RemoveParentBiDirection(node.Parent[j]);
                                    }
                                    for (int j = node.Child.Count - 1; j >= 0; j--) {
                                        node.RemoveChildBiDirection(node.Child[j]);
                                    }  
                                    maxtest.lattice.RemoveAt(i);
                                }
                            }
                            //if (maxtest.lattice.Contains(temp)) {
                            //    Console.WriteLine("existed");
                            //} else {
                            //    Console.WriteLine("not existed");
                            //}
                            index++;
                            totalIndex++;

                        }
                        Console.WriteLine("Trans {0} Nodes {1} Time ", READ_NUM_TRANS + increTotalSize, maxtest.lattice.Count);
                        dic.Add(READ_NUM_TRANS + increTotalSize, sw.ElapsedMilliseconds);
                        increTotalSize += INCRE_SIZE;
                    }


                }
            }
        }


        public void ParseArgs(string[] args, string infile, string outfile) {
            //        void parse_args(int argc, char **argv)
            //{
            //   extern char * optarg;
            //   int c;
            //if (args == null || args.Length < 2) {
            //    Console.Write("usage: charm\n");
            //    Console.Write("\t-i<infile>\n");
            //    Console.Write("\t-s<support>\n");
            //    Console.Write("\t-S<absolute support> (specify -s or -S)\n");
            //    //cout << "\t-a<algtype> (<0=eclat>, 1=charm, 2=basicmax, 3=maxcharm)\n";
            //    //cout << "\t-c<closedtype> (0=cnone, <1=chash>, 2=cmax)\n";
            //    Console.Write("\t-d<difftype> (0=nodiff, 1=diff, <2=diff2>, 3=diffin)\n");
            //    //cout << "\t-m<maxdifftype> (<0=nodiff>, 1=diff, 2=diff2, 3=diffin)\n";
            //    Console.Write("\t-p<prunetype> (<0=noprune>, 1=prune)\n");
            //    Console.Write("\t-C<constraints file>\n");
            //    Console.Write("\t-z<sorttype> (0=nosort, <1=incr>, 2=decr, 3=incr_noclass)\n");
            //    Console.Write("\t-o <outfile (if COUT, then stdout is used)>\n");
            //    Console.Write("\t-l (output + output_tidlist?)\n");
            //    Console.Write("\t-L (output lattice?)\n");
            //    Console.Write("\t-M<mingentype> (<0=minhitset>, 1=apriori) \n");
            //    Console.Write("\t-n<stop after mingen phase>\n");
            //    Console.Write("\t-b (binary input file?) (default=false)\n");
            //    Console.Write("\t-x (no offset (non-ibm-format?) (default=false/ibm)\n");
            //    Console.Write("\t-r <minconf> (generate rules? at what conf level?)\n");
            //    Console.Write("\t-R (print rules?)\n");
            //    Console.Write("\t-E (self rules only)\n");
            //    Console.Write("\t-F (conditional self rules also)\n");
            //    Console.Write("\t-Z <maxlen>\n");
            //    return;
            //} else {
            for (int i = 0; i < args.Length; i = i + 2) {
                if (i + 1 >= args.Length || args[i].Length <= 1) {
                    break;
                }
                char c = args[i].Substring(1, 1)[0];
                string value = args[i + 1];
                switch (c) {
                    case 'a': {
                            //only support charm
                            break;
                        }
                    case 'b': {
                            Dbase_Ctrl_Blk.binary_input = true;
                            break;
                        }
                    case 'c': {
                            Paramenters.closed_type = (closed_vals)(int.Parse(value));
                            break;
                        }
                    case 'C': {
                            Paramenters.check_constraints = true;
                            Paramenters.consfile = value;
                            break;
                        }
                    case 'd': {
                            Paramenters.diff_type = (diff_vals)int.Parse(value);
                            //    diff_type = (diff_vals)atoi(optarg);
                            break;
                        }
                    case 'i': //input files 
                            {
                            Paramenters.infile = value;
                            if (!string.IsNullOrEmpty(infile)) {
                                Paramenters.infile = infile;
                            }
                            break;
                            //    sprintf(infile, "%s", optarg);
                            //    break;
                        }
                    case 'l': //print idlists along with freq subtrees
                            {
                                Paramenters.output_idlist = true;
                                break;
                            }
                    case 'L': {
                                Paramenters.output_lattice = true;
                                break;
                            }
                    case 'M': {
                            Paramenters.mingen_type = (mingen_vals)int.Parse(value);
                            break;
                        }
                    case 'n': {
                            Paramenters.mingen_only = true;
                            break;
                        }
                    case 'm': {
                            Paramenters.max_diff_type = (diff_vals)int.Parse(value);
                            break;
                        }
                    case 'o': //print freq subtrees
                            {
                            Paramenters.output = true;
                            Paramenters.outfile = value;
                            if (!string.IsNullOrEmpty(outfile)) {
                                Paramenters.outfile = outfile;
                            }
                            //writer = new StreamWriter(Paramenters.outfile);
                            break;
                        }
                    case 'p': {
                                Paramenters.prune_type = (prune_vals)int.Parse(value);
                                break;
                            }
                    case 'r': {
                            Paramenters.rulegen = true;
                            MINCONF = double.Parse(value);
                            break;
                        }
                    case 'R':
                        Paramenters.print_rules = true;
                        break;
                    case 'E':
                        Paramenters.self_rules_only = true;
                        break;
                    case 'F':
                        Paramenters.conditional_self_rules = true;
                        break;
                    case 's': //support value for L2
                        MINSUP_PER = double.Parse(value, CultureInfo.InvariantCulture);
                        break;
                    case 'S': //absolute support
                        MINSUPPORT = int.Parse(value);
                        break;
                    case 'x':
                        Dbase_Ctrl_Blk.nooffset = true;
                        break;
                    case 'z':

                        Paramenters.sort_type = (sort_vals)int.Parse(value);
                        break;
                    case 'Z':
                        Paramenters.max_closed_len = int.Parse(value);
                        break;

                }
            }

            if (!string.IsNullOrEmpty(infile)) {
                Paramenters.infile = infile;
            }


            if (!string.IsNullOrEmpty(outfile)) {
                Paramenters.outfile = outfile;
            }
            //writer = new StreamWriter(Paramenters.outfile);

            //}

        }

        public void Run(string[] args, string infile, string outfile, Dictionary<int, long> dic) {

            ParseArgs(args, infile, outfile);
            //Dictionary<int, long> dic = new Dictionary<int, long>();
            Console.WriteLine("Number of lattice node : {0}", maxtest.lattice.Count);
            DateTime start = DateTime.Now;
            using (StreamWriter outFile = new StreamWriter(outfile)) {
                //           ofstream summary("summary.out", ios::app);
                switch (Paramenters.alg_type) {
                    case alg_vals.basicmax: outFile.Write("MAX "); break;
                    case alg_vals.charm: {
                            outFile.Write("CHARM ");
                            switch (Paramenters.closed_type) {
                                case closed_vals.cnone: break;
                                case closed_vals.chash: outFile.Write("CHASH "); break;
                                case closed_vals.cmax: outFile.Write("CMAX "); break;
                            }
                            break;
                        }
                    case alg_vals.maxcharm: outFile.Write("MAXCHARM "); break;
                    case alg_vals.eclat: outFile.Write("ECLAT "); break;
                    case alg_vals.colex: outFile.Write("COLEX "); break;
                }
                switch (Paramenters.diff_type) {
                    case diff_vals.nodiff: outFile.Write("NODIFF "); break;
                    case diff_vals.diff: outFile.Write("DIFF "); break;
                    case diff_vals.diff2: outFile.Write("DIFF2 "); break;
                    case diff_vals.diffin: outFile.Write("DIFFIN "); break;

                }
                switch (Paramenters.max_diff_type) {
                    case diff_vals.nodiff: outFile.Write("MNODIFF "); break;
                    case diff_vals.diff: outFile.Write("MDIFF "); break;
                    case diff_vals.diff2: outFile.Write("MDIFF2 "); break;
                    case diff_vals.diffin: outFile.Write("MDIFFIN "); break;
                }
                switch (Paramenters.sort_type) {
                    case sort_vals.nosort: outFile.Write("NOSORT "); break;
                    case sort_vals.incr: outFile.Write("INCR "); break;
                    case sort_vals.incr_noclass: outFile.Write("INCRNOC "); break;
                    case sort_vals.decr: outFile.Write("DECR "); break;
                }
                switch (Paramenters.prune_type) {
                    case prune_vals.prune: outFile.Write("PRUNE "); break;
                    case prune_vals.noprune: break;
                }

                if (Paramenters.check_constraints) {
                    constraints = new Constraints(Paramenters.consfile);
                }

                DCB = new Dbase_Ctrl_Blk(Paramenters.infile);
                //add to max test
                maxtest.dbase = DCB;
                using (MyStopWatch sw = new MyStopWatch()) {
                    Get_F1();
                    List<Eqclass> F2list = get_F2();
                    get_Fk(F2list);

                    Console.WriteLine("Number of lattice node : {0}", maxtest.lattice.Count);

                    Console.WriteLine("Init with CHARM-L costs ");
                    dic.Add(READ_NUM_TRANS, sw.ElapsedMilliseconds);
                }
                IncreLattice(dic);
                //writer.Close();
                foreach (var item in maxtest.lattice) {
                    outFile.WriteLine(string.Join(" ", item.Items) + " " + item.Sup);

                }
            }
        }


        private void BuildIndexTable(IndexTable table, LatticeNode lattice, int num) {
            List<LatticeNode> nodes = new List<LatticeNode>();
            nodes.Add(lattice);
            while (nodes.Count != 0) {
                LatticeNode node = nodes[0];
                if (table.Indexs.Count == num) { return; }
                foreach (var item in node.Items) {
                    if (!table.ContainsKey(item)) {
                        table.AddIndex(item, node);
                    }
                }
                nodes.AddRange(node.Child);
                nodes.RemoveAt(0);
            }
            //foreach (var item in lattice.Items) {
            //    if (!table.ContainsKey(item)) {
            //        table.AddIndex(item, lattice);
            //    }
            //}
            //foreach (LatticeNode child in lattice.Child) {
            //    BuildIndexTable(table, child);
            //}

        }

        private void get_Fk(List<Eqclass> F2list) {
            //Eqclass* eq;
            //idlist newmax;
            //int iter = 2;
            //int osz;
            Eqclass eq;
            List<int> newmax;
            int iter = 2;
            int osz;
            while (F2list.Count() > 0) {
                eq = F2list[0];
                LatticeNode ln = form_closed_f2_lists(ref eq, lattice);
                newmax = new List<int>();
                //if (ln != null) ln = lattice;
                if (ln == null) ln = lattice;
                enumerate_closed_freq(ref eq, iter + 1, newmax, ln);
                F2list.RemoveAt(0);
            }
        }

        private void enumerate_closed_freq(ref Eqclass eq, int iter, List<int> newmax, LatticeNode lattice = null) {

            Eqclass neq;
            int nmaxpos;
            bool cflg = false;
            //list<Eqnode *>::iterator ni, nj;
            Eqnode join;
            subset_vals sval;

            bool addnodes = true;
            //if the eqclass as a whole does not contain the col constraints,
            //then no point proceeding
            if (Paramenters.check_constraints && !constraints.CheckColConstraints(eq, addnodes))
                return;

            nmaxpos = newmax.Count; //initial newmax pos
            eq.SortNodes();
            //print_tabs(iter-3);
            //cout << "F" << iter << " " << *eq;
            int ni, nj;
            //for (ni = eq.nList.begin(); ni != eq->nlist().end(); ++ni){
            for (ni = 0; ni != eq.nList.Count; ++ni) {
                neq = new Eqclass();
                //if (eq.Prefix.Contains(eq.nList[ni].Val)) {
                //    eq.nList.RemoveAt(ni);
                //    ni--;
                //    continue;
                //}


                neq.SetPrefix(eq.Prefix, eq.nList[ni]);//->set_prefix(eq->prefix(),*(*ni));

                if (Paramenters.closed_type == closed_vals.cmax)
                    maxtest.update_maxset(ni, eq, newmax, nmaxpos, ref cflg);

                nmaxpos = newmax.Count; //initial newmax pos
                nj = ni;
                //if (nj == eq.nList.Count - 1) {
                //    //PTL: this case the loop dont reach

                //    //recalculate the eq.nList[ni];
                //    join = new Eqnode(eq.nList[nj].Val);
                //    sval = get_join(eq.nList[ni], eq.nList[nj], join, iter);
                //}
                for (++nj; nj < eq.nList.Count; ) {
                    join = new Eqnode(eq.nList[nj].Val);

                    sval = get_join(eq.nList[ni], eq.nList[nj], join, iter);


                    if (notfrequent(join)) {
                        //delete join;
                        //eq.nList.RemoveAt(nj);
                        ++nj;
                    } else if (Paramenters.check_constraints && !constraints.CheckRowConstraints(join.TidSet, iter)) {
                        //delete join;
                        ++nj;
                    } else {
                        //         stats.incrlarge(iter-1);
                        switch (sval) {
                            case subset_vals.subset:
                                //add nj to all elements in eq by adding nj to prefix                                
                                neq.Prefix.Add(eq.nList[nj].Val);


                                //delete join;
                                ++nj;
                                break;
                            case subset_vals.notequal:
                                if (Paramenters.closed_type == closed_vals.cmax) get_max_join(eq.nList[ni], eq.nList[nj], join, iter);
                                if (neq.Prefix.Count + 1 <= Paramenters.max_closed_len) {
                                    neq.AddNode(join);
                                }
                                ++nj;
                                break;
                            case subset_vals.equals:
                                //add nj to all elements in eq by adding nj to prefix
                                neq.Prefix.Add(eq.nList[nj].Val);
                                //delete* nj;
                                eq.nList.RemoveAt(nj);
                                //nj = eq->nlist().erase(nj); //remove nj
                                //cout << "EQUAL " << *join << endl;
                                //delete join;
                                break;
                            case subset_vals.superset:
                                if (Paramenters.closed_type == closed_vals.cmax) get_max_join(eq.nList[ni], eq.nList[nj], join, iter);
                                //delete *nj;
                                //nj = eq->nlist().erase(nj); //remove nj
                                eq.nList.RemoveAt(nj);
                                //++nj;                                
                                neq.AddNode(join);
                                //neq->add_node(join);            
                                break;
                        }
                    }
                }
                if (lattice == null) lattice = new LatticeNode();
                LatticeNode ln = null;
                cflg = true;
                if (Paramenters.closed_type == closed_vals.cmax) {
                    // if (!eq.Prefix.Contains(eq.nList[ni].Val)) {
                    cflg = maxtest.check_closed(eq.nList[ni]);

                    if (cflg) {
                        ln = lattice.AddChild(neq.Prefix, eq.nList[ni].Sup);
                        maxtest.adjustlattice(ln, eq.nList[ni].MaxSet);
                        maxtest.addnode(neq.Prefix, -1, ln, eq.nList[ni].Sup);
                        newmax.Add(maxtest.maxcount - 1);
                    }
                    //}
                } else if (Paramenters.closed_type == closed_vals.chash) {
                    cflg = hashtest.Add(neq.Prefix, -1, eq.nList[ni].Sup, eq.nList[ni].Hval);
                }

                //TODO
                //if (cflg) {
                //    //stats.incrmax(neq->prefix().size() - 1);
                //    if (Paramenters.outfile) {
                //        if (!Paramenters.check_constraints ||
                //            (Paramenters.check_constraints && constraints.CheckColConstraints(neq))) {
                //            //neq.PrintPrefix();
                //            outfile << "- " << (*ni)->sup;
                //            if (output_idlist) (*ni)->print_node(outfile, false);
                //            outfile << endl;
                //        }
                //    }
                //}

                //   stats.incrtime(iter-1, tt.Stop());

                if (neq.nList.Count > 1) {
                    //                    if (ln != null) ln = lattice;
                    if (ln == null) ln = lattice;
                    enumerate_closed_freq(ref neq, iter + 1, newmax, ln);
                } else if (neq.nList.Count == 1) {
                    //if the eqclass as a whole does not contain the col constraints,
                    //then no point proceeding
                    addnodes = true;
                    cflg = true;
                    if (Paramenters.closed_type == closed_vals.cmax) {
                        // if (!neq.Prefix.Contains(neq.nList[0].Val)) {
                        cflg = maxtest.check_closed(neq.nList[0]);
                        if (cflg) {
                            LatticeNode ln2;
                            //if (ln)
                            if (ln != null) {
                                ln2 = ln.AddChild(neq.Prefix, neq.nList[0].Val, neq.nList[0].Sup);
                            } else {
                                ln2 = lattice.AddChild(neq.Prefix, neq.nList[0].Val, neq.nList[0].Sup);
                            }
                            maxtest.adjustlattice(ln2, neq.nList[0].MaxSet);
                            maxtest.addnode(neq.Prefix, neq.nList[0].Val,
                                            ln2, neq.nList[0].Sup);
                            newmax.Add(maxtest.maxcount - 1);
                        }
                        //   }
                    } else if (Paramenters.closed_type == closed_vals.chash) {
                        cflg = hashtest.Add(neq.Prefix, neq.nList[0].Val, neq.nList[0].Sup, neq.nList[0].Hval);
                    }

                    //      if (cflg){
                    //         if (output){
                    //            if (!check_constraints || 
                    //                (check_constraints && 
                    //                 constraints->checkColConstraints(neq,addnodes))){
                    //               outfile << *neq;
                    //            }

                    //            stats.incrmax(neq->prefix().size());
                    //         }
                    //      }
                }
                //neq.nList =  new List<Eqnode>();
                //delete neq;
                //neq = new Eqclass();
            }
        }

        private void get_max_join(Eqnode l1, Eqnode l2, Eqnode join, int iter) {
            //throw new NotImplementedException();
            int idsum = 0;
            //find local maximal context for join
            //i.e., which maximal sets contain join as a subset
            switch (Paramenters.max_diff_type) {
                case diff_vals.nodiff:
                    get_intersect(l1.MaxSet, l2.MaxSet, join.MaxSet, ref idsum);
                    join.MaxSup = join.MaxSet.Count;
                    break;
                case diff_vals.diff2:
                    if (iter == 2) get_diff(l1.MaxSet, l2.MaxSet, join.MaxSet, ref idsum);
                    else get_diff(l2.MaxSet, l1.MaxSet, join.MaxSet, ref idsum);
                    join.MaxSup = l1.MaxSup - join.MaxSet.Count;
                    break;
                case diff_vals.diffin:
                    get_diff(l2.MaxSet, l1.MaxSet, join.MaxSet, ref idsum);
                    join.MaxSup = l1.MaxSup - join.MaxSet.Count;
                    break;
                case diff_vals.diff:
                    Console.WriteLine("diff NOT HANDLED\n");
                    //exit(-1);
                    break;
            }
        }

        private LatticeNode form_closed_f2_lists(ref Eqclass eq, LatticeNode lattice) {
            bool addnodes = true;
            ////if the eqclass as a whole does not contain the col constraints,
            ////then no point proceeding
            BitArray bvec = new BitArray(DCB.NumF1, true);
            if (Paramenters.check_constraints && !constraints.CheckColConstraints(eq, addnodes)) {
                return null;
            }
            subset_vals sval;
            Eqnode l1, l2;
            int pit, nit;
            bool extend = false;
            bool cflg;

            pit = eq.Prefix[0];
            l1 = DCB.ParentClass[pit];

            if (Paramenters.check_constraints) {
                int iter = 1;
                if (!constraints.CheckRowConstraints(l1.TidSet, iter)) {
                    eq.nList = new List<Eqnode>();
                    return null;
                }
            }

            //bvec is set to false if the item has already been incorporated
            //into a "composite" item via the superset/equal case below
            //such an item can be skipped
            if (!(bvec[pit])) {
                eq.nList = new List<Eqnode>();
                return null;
            }
            int ni;

            for (ni = 0; ni != eq.nList.Count; ) {//- 1 && eq.nList.Count - 1 >= 0; ) {
                nit = eq.nList[ni].Val;
                if (!bvec[nit]) {
                    eq.nList.RemoveAt(ni);
                    continue;
                }
                l2 = DCB.ParentClass[nit];

                sval = get_join(l1, l2, eq.nList[ni], 2);

                if (notfrequent(eq.nList[ni])) {
                    eq.nList.RemoveAt(ni);
                    continue;
                } else if (Paramenters.check_constraints) {
                    int iter = 2;
                    if (!constraints.CheckRowConstraints(eq.nList[ni].TidSet, iter)) {
                        eq.nList.RemoveAt(ni);
                        continue;
                    }
                }


                switch (sval) {
                    case subset_vals.subset:
                        //add nj to all elements in eq by adding nj to prefix
                        eq.Prefix.Add(eq.nList[ni].Val);
                        extend = true;

                        eq.nList.RemoveAt(ni);

                        break;
                    case subset_vals.notequal:
                        if (Paramenters.alg_type == alg_vals.maxcharm || Paramenters.closed_type == closed_vals.cmax) {
                            get_max_join(l1, l2, eq.nList[ni], 2);
                        }
                        ++ni;
                        break;
                    case subset_vals.equals:
                        //add nj to all elements in eq by adding nj to prefix
                        eq.Prefix.Add(eq.nList[ni].Val);
                        extend = true;
                        eq.nList.RemoveAt(ni);
                        bvec[nit] = false;
                        break;
                    case subset_vals.superset:
                        bvec[nit] = false;
                        if (Paramenters.alg_type == alg_vals.maxcharm || Paramenters.closed_type == closed_vals.cmax) {
                            get_max_join(l1, l2, eq.nList[ni], 2);
                        }
                        ++ni;
                        break;
                }
            }


            LatticeNode ln = null;
            if (Paramenters.alg_type == alg_vals.charm) {


                cflg = true;
                if (Paramenters.closed_type == closed_vals.cmax) {
                    cflg = maxtest.check_closed(l1);
                    if (cflg) {
                        ln = lattice.AddChild(eq.Prefix, l1.Sup);
                        maxtest.adjustlattice(ln, l1.MaxSet);
                        maxtest.addnode(eq.Prefix, -1, ln, l1.Sup);
                    }
                } else if (Paramenters.closed_type == closed_vals.chash) {
                    cflg = hashtest.Add(eq.Prefix, -1, l1.Sup, l1.Hval);
                }

                if (eq.nList.Count == 1) {
                    //if the eqclass as a whole does not contain the col constraints,
                    //then no point proceeding
                    addnodes = true;
                    cflg = true;
                    if (Paramenters.closed_type == closed_vals.cmax) {
                        cflg = maxtest.check_closed(eq.nList[0]);
                        if (cflg) {
                            LatticeNode ln2;
                            if (ln != null) {
                                ln2 = ln.AddChild(eq.Prefix,
                                                    eq.nList[0].Val,
                                                    eq.nList[0].Sup);

                            } else {
                                ln2 = lattice.AddChild(eq.Prefix,
                                                         eq.nList[0].Val,
                                                    eq.nList[0].Sup);
                            }
                            maxtest.adjustlattice(ln2, eq.nList[0].MaxSet);
                            maxtest.addnode(eq.Prefix, eq.nList[0].Val, ln2,
                                            eq.nList[0].Sup);
                        }
                    } else if (Paramenters.closed_type == closed_vals.chash) {
                        cflg = hashtest.Add(eq.Prefix, eq.nList[0].Val,
                                            eq.nList[0].Sup,
                                            eq.nList[0].Sup);
                    }
                    eq.nList = new List<Eqnode>();
                }
            }
            return ln;
        }

        bool notfrequent(Eqnode n) {

            if (n.Sup >= MINSUPPORT) return false;
            else return true;
        }

        private subset_vals get_join(Eqnode l1, Eqnode l2, Eqnode join, int iter) {

            subset_vals sval = subset_vals.notequal;

            int diffmax = l1.Sup - MINSUPPORT;

            int idsum = 0;


            //compute tidset or diffset for join of l1 nd l2
            switch (Paramenters.diff_type) {
                case diff_vals.diff2:
                    if (iter == 2) sval = get_diff(l1.TidSet, l2.TidSet,
                                                   join.TidSet, ref idsum, diffmax);
                    else sval = get_diff(l2.TidSet, l1.TidSet, join.TidSet,
                                        ref idsum, diffmax);
                    if (sval == subset_vals.subset) sval = subset_vals.superset;
                    else if (sval == subset_vals.superset) sval = subset_vals.subset;
                    join.Sup = l1.Sup - join.TidSet.Count;
                    join.Hval = l1.Hval - idsum;
                    break;
                case diff_vals.nodiff:
                    sval = get_intersect(l1.TidSet, l2.TidSet,
                                         join.TidSet, ref idsum, MINSUPPORT);
                    join.Sup = join.TidSet.Count;
                    join.Hval = idsum;
                    break;
                case diff_vals.diffin:
                    sval = get_diff(l2.TidSet, l1.TidSet, join.TidSet, ref idsum, diffmax);
                    if (sval == subset_vals.subset) sval = subset_vals.superset;
                    else if (sval == subset_vals.superset) sval = subset_vals.subset;
                    join.Sup = l1.Sup - join.TidSet.Count;
                    join.Hval = l1.Hval - idsum;
                    break;
                case diff_vals.diff:
                    if (iter == 2) {
                        sval = get_intersect(l1.TidSet, l2.TidSet,
                                             join.TidSet, ref idsum, MINSUPPORT);
                        join.Sup = join.TidSet.Count;
                        join.Hval = idsum;
                    } else {
                        if (iter == 3)
                            sval = get_diff(l1.TidSet, l2.TidSet, join.TidSet,
                                            ref idsum, diffmax);
                        else
                            sval = get_diff(l2.TidSet, l1.TidSet, join.TidSet,
                                          ref  idsum, diffmax);
                        if (sval == subset_vals.subset) sval = subset_vals.superset;
                        else if (sval == subset_vals.superset) sval = subset_vals.subset;
                        join.Sup = l1.Sup - join.TidSet.Count;
                        join.Hval = l1.Hval - idsum;
                    }
                    break;
            }

            return sval;

        }


        //lay phan giao
        private subset_vals get_intersect(List<int> l1, List<int> l2, List<int> join, ref int idsum, int minsup = 0) {
            int diffmax1, diffmax2;
            diffmax1 = l1.Count - minsup;
            diffmax2 = l2.Count - minsup;

            int diffcnt1 = 0, diffcnt2 = 0;
            int n1, n2;
            int i1 = 0, i2 = 0;

            idsum = 0;
            while (i1 < l1.Count && i2 < l2.Count &&
                   diffcnt1 <= diffmax1 && diffcnt2 <= diffmax2) {
                n1 = l1[i1];
                n2 = l2[i2];

                //look for matching cids
                if (n1 < n2) {
                    ++i1;
                    ++diffcnt1;
                } else if (n1 > n2) {
                    ++i2;
                    ++diffcnt2;
                } else {
                    if (join != null) join.Add(n1);
                    idsum += n1;
                    ++i1;
                    ++i2;
                }
            }

            if (i1 < l1.Count) ++diffcnt1;
            if (i2 < l2.Count) ++diffcnt2;

            if (diffcnt1 == 0 && diffcnt2 == 0) return subset_vals.equals;
            else if (diffcnt1 == 0 && diffcnt2 > 0) return subset_vals.subset;
            else if (diffcnt1 > 0 && diffcnt2 == 0) return subset_vals.superset;
            else return subset_vals.notequal;
        }

        /// <summary>
        /// //performs l1 - l2
        /// </summary>
        /// <param name="l1"></param>
        /// <param name="l2"></param>
        /// <param name="join"></param>
        /// <param name="idsum"></param>
        /// <param name="diffmax"></param>
        /// <returns></returns>
        subset_vals get_diff(List<int> l1, List<int> l2, List<int> join, ref int idsum, int diffmax = Constants.INT_MAX) {
            int n1, n2;
            int diffcnt1 = 0, diffcnt2 = 0;
            int i1 = 0, i2 = 0;

            idsum = 0;

            while (i1 < l1.Count && i2 < l2.Count && diffcnt1 <= diffmax) {
                n1 = l1[i1];
                n2 = l2[i2];

                if (n1 < n2) {
                    //implies that n1 is not to be found in n2
                    join.Add(n1);
                    ++diffcnt1;
                    idsum += n1;

                    ++i1;
                } else if (n1 > n2) {
                    ++i2;
                    ++diffcnt2;
                } else {
                    ++i1;
                    ++i2;
                }
            }

            //add any remaining elements in l1 to join
            while (i1 < l1.Count) {
                join.Add(l1[i1]);
                idsum += l1[i1];
                ++i1;
                ++diffcnt1;
            }

            if (i2 < l2.Count) ++diffcnt2;

            if (diffcnt1 == 0 && diffcnt2 == 0) return subset_vals.equals;
            else if (diffcnt1 == 0 && diffcnt2 > 0) return subset_vals.superset;
            else if (diffcnt1 > 0 && diffcnt2 == 0) return subset_vals.subset;
            else return subset_vals.notequal;
        }


        private List<Eqclass> get_F2() {
            int i, j;
            int it1, it2;
            //  list<Eqclass *> *F2list = new list<Eqclass *>;
            List<Eqclass> F2list = new List<Eqclass>();
            //itcnt2 is a matrix of pairs p, p.first is count, p.second is flag

            List<List<int>> itcnt2 = new List<List<int>>();

            for (i = 0; i < DCB.NumF1; ++i) {

                itcnt2.Add(new List<int>(DCB.NumF1));

                for (j = 0; j < DCB.NumF1; ++j) {
                    itcnt2[i].Add(0);
                }
            }
            int temp = 1;

            while (DCB.GetNextTrans() && temp < READ_NUM_TRANS) {
                DCB.GetValidTrans();

                DCB.MakeVertical();

                //count a pair only once per cid
                for (i = 0; i < DCB.TransSz; ++i) {
                    it1 = DCB.TransAry[i];
                    for (j = i + 1; j < DCB.TransSz; ++j) {
                        it2 = DCB.TransAry[j];
                        ++itcnt2[it1][it2];
                    }
                }
                temp++;
            }

            //compute class size
            Dbase_Ctrl_Blk.class_sz = new List<int>(DCB.NumF1);// new int[DCB->NumF1];
            Dbase_Ctrl_Blk.F2sum = new List<int>(DCB.NumF1); //new int[DCB->NumF1];
            for (i = 0; i < DCB.NumF1; ++i) {
                Dbase_Ctrl_Blk.class_sz.Add(0);
                Dbase_Ctrl_Blk.F2sum.Add(0);
            }

            for (i = 0; i < DCB.NumF1; ++i) {
                for (j = i + 1; j < DCB.NumF1; ++j) {
                    if (itcnt2[i][j] >= MINSUPPORT) {
                        //              cout << "TEST " << DCB->FreqIdx[i] << " " << DCB->FreqIdx[j] 
                        //                   << " - " << itcnt2[i][j] << endl;
                        ++Dbase_Ctrl_Blk.class_sz[i];
                        ++Dbase_Ctrl_Blk.class_sz[j];
                        Dbase_Ctrl_Blk.F2sum[i] += itcnt2[i][j];
                        Dbase_Ctrl_Blk.F2sum[j] += itcnt2[i][j];
                    }
                }
            }

            DCB.sort_ParentClass();
            //Console.WriteLine("Sort Order :");


            //for (i = 0; i < DCB.NumF1; ++i) {
            //    Console.WriteLine(" " + DCB.FreqIdx[DCB.ParentClass[i].Val]);
            //}
            //Console.WriteLine();

            //for (i = 0; i < DCB.NumF1; ++i)
            //    Console.Write(" " + Dbase_Ctrl_Blk.class_sz[DCB.ParentClass[i].Val]);
            //Console.WriteLine();


            //for (i = 0; i < DCB.NumF1; ++i)
            //    Console.Write(" " + Dbase_Ctrl_Blk.F2sum[DCB.ParentClass[i].Val]);
            //Console.WriteLine();

            //Console.WriteLine("Class sort {0} {1} {2} {3}", i, DCB.ParentClass[i].Val, DCB.FreqIdx[DCB.ParentClass[i].Val], DCB.ParentClass[i].Sup);


            int F2cnt = 0;

            // count frequent patterns and generate eqclass

            Eqclass eq;
            int sup;
            for (i = 0; i < DCB.NumF1; ++i) {
                eq = new Eqclass();
                eq.Prefix.Add(i);

                it1 = DCB.ParentClass[i].Val;
                for (j = i + 1; j < DCB.NumF1; ++j) {
                    //Console.WriteLine("Access {0} {1}", i, j);

                    it2 = DCB.ParentClass[j].Val;
                    if (it1 < it2) sup = itcnt2[it1][it2];
                    else sup = itcnt2[it2][it1];
                    if (sup >= MINSUPPORT) {
                        ++F2cnt;
                        eq.AddNode(j);
                        if (Paramenters.output && Paramenters.alg_type == alg_vals.eclat) {
                            //outfile << DCB->FreqIdx[it1] << " " << DCB->FreqIdx[it2] 
                            //     << " - " << sup << endl;

                        }
                    }
                }
                F2list.Add(eq);
            }



            //remap FreqIdx, FreqMap and ParentClass vals in sorted order
            //Console.WriteLine("FREQMAP : ");
            for (i = 0; i < DCB.NumF1; ++i) {
                DCB.FreqMap[DCB.FreqIdx[DCB.ParentClass[i].Val]] = i;
                //Console.WriteLine("{0} {1} {2} {3}", i, DCB.ParentClass[i].Val, DCB.FreqIdx[DCB.ParentClass[i].Val], DCB.FreqMap[DCB.FreqIdx[DCB.ParentClass[i].Val]]);
            }




            //   for (i=0; i < DBASE_MAXITEM; ++i)
            //      if (DCB->FreqMap[i] != -1){
            //         DCB->FreqIdx[DCB->FreqMap[i]] = i;
            //         //cout << i << " " << DCB->FreqMap[i] 
            //         //     << " " << DCB->FreqIdx[DCB->FreqMap[i]] << endl;
            //      }

            for (i = 0; i < DBASE_MAXITEM; ++i)
                if (DCB.FreqMap[i] != -1) {
                    DCB.FreqIdx[DCB.FreqMap[i]] = i;
                    //Console.WriteLine("{0} {1} {2}", i, DCB.FreqMap[i], DCB.FreqIdx[DCB.FreqMap[i]]);

                }
            //   //cout << "ORDER :";
            //   for (i=0; i < DCB->NumF1; ++i){
            //      DCB->ParentClass[i]->val = i;
            //      //cout << " " << DCB->FreqIdx[i];
            //   }
            Console.WriteLine("ORDER :");
            for (i = 0; i < DCB.NumF1; ++i) {
                DCB.ParentClass[i].Val = i;
                Console.Write(" " + DCB.FreqIdx[i]);
            }


            //   //cout << "F2 - " << F2cnt << " " << DCB->NumF1 * DCB->NumF1 << endl;
            //   te = tt.Stop();
            //   stats.add(DCB->NumF1 * DCB->NumF1, F2cnt, te);
            return F2list;
            //   return F2list;
            //}
        }

        public void Get_F1() {
            //            void get_F1()
            //{
            //  TimeTracker tt;
            //  double te;
            int i, j, it;
            //  int i, j, it;
            const int arysz = 10;

            //  vector<int> itcnt(arysz,0); //count item frequency
            List<int> itcnt = new List<int>(arysz);
            for (int k = 0; k < 100000; k++) {
                itcnt.Add(0);
            }

            //  tt.Start();
            DBASE_MAXITEM = 0;
            DBASE_NUM_TRANS = 0;

            //  DBASE_MAXITEM=0;
            //  DBASE_NUM_TRANS = 0;
            DCB.Cidsum = 0;
            int temp = 1;
            while (DCB.GetNextTrans() && temp < READ_NUM_TRANS) {
                for (i = 0; i < DCB.TransSz; ++i) {
                    it = DCB.TransAry[i];

                    if (it >= DBASE_MAXITEM) {
                        //itcnt.resize(it + 1, 0);
                        DBASE_MAXITEM = it + 1;
                        //cout << "IT " << DBASE_MAXITEM << endl;
                    }
                    ++itcnt[it];
                }
                DCB.TransSz = DCB.TransAry.Count;
                if (DCB.MaxTransSz < DCB.TransSz) DCB.MaxTransSz = DCB.TransSz;
                ++DBASE_NUM_TRANS;
                DCB.Cidsum += DCB.Cid;
                temp++;
            }

            DCB.reset();
            //  DCB->Cidsum = 0;

            //   while(DCB->get_next_trans())
            //   {
            //      //cout << DCB->Cid << " " << DCB->TransSz << endl;
            //      for (i=0; i < DCB->TransSz; ++i){
            //         it = DCB->TransAry[i];

            //         if (it >= DBASE_MAXITEM){
            //            itcnt.resize(it+1,0);
            //            DBASE_MAXITEM = it+1;
            //            //cout << "IT " << DBASE_MAXITEM << endl;
            //         }
            //         ++itcnt[it];
            //      }

            //      if (DCB->MaxTransSz < DCB->TransSz) DCB->MaxTransSz = DCB->TransSz;     
            //      ++DBASE_NUM_TRANS;
            //      DCB->Cidsum += DCB->Cid; //used to initialize hashval for closed set mining
            //   }

            //   //set the value of MINSUPPORT
            //   if (MINSUPPORT == -1)
            //     MINSUPPORT = (int) (MINSUP_PER*DBASE_NUM_TRANS+0.5);
            int increSize = 0;
            if (MINSUPPORT == -1) {
                MINSUPPORT = (int)(MINSUP_PER * (DBASE_NUM_TRANS + increSize) + 0.5);
            }
            //   if (MINSUPPORT<1) MINSUPPORT=1;
            if (MINSUPPORT < 1) {
                MINSUPPORT = 1;
            }


            MINSUPPORT -= increSize;
            MINSUP_PER = Math.Round(MINSUPPORT * 1.0 / DBASE_NUM_TRANS, 2);
            Console.WriteLine("DBASE_NUM_TRANS : {0}", DBASE_NUM_TRANS);
            Console.WriteLine("DBASE_MAXITEM : {0}", DBASE_MAXITEM);
            Console.WriteLine("MINSUPPORT : {0} ({1})", MINSUPPORT, MINSUP_PER);

            //   cout<<"DBASE_NUM_TRANS : "<< DBASE_NUM_TRANS << endl;
            //   cout<<"DBASE_MAXITEM : "<< DBASE_MAXITEM << endl;
            //   cout<<"MINSUPPORT : "<< MINSUPPORT << " (" << MINSUP_PER << ")" << endl;

            //   //count number of frequent items
            //   DCB->NumF1 = 0;
            //   for (i=0; i < DBASE_MAXITEM; ++i)
            //     if (itcnt[i] >= MINSUPPORT)
            //       ++DCB->NumF1;
            DCB.NumF1 = 0;
            for (i = 0; i < DBASE_MAXITEM; i++) {
                if (itcnt[i] >= MINSUPPORT) {
                    ++DCB.NumF1;
                }
            }
            //   //construct forward and reverse mapping from items to freq items

            //   DCB->FreqIdx = new int [DCB->NumF1];
            //   DCB->FreqMap = new int [DBASE_MAXITEM];
            DCB.FreqIdx = new List<int>();
            for (i = 0; i < DCB.NumF1; i++) {
                DCB.FreqIdx.Add(0);
            }
            DCB.FreqMap = new List<int>();
            for (i = 0; i < DBASE_MAXITEM; i++) {
                DCB.FreqMap.Add(0);
            }
            //   for (i=0,j=0; i < DBASE_MAXITEM; ++i) {
            //      if (itcnt[i] >= MINSUPPORT) {
            //         if (output && alg_type == eclat) 
            //            outfile << i << " - " << itcnt[i] << endl;
            //         DCB->FreqIdx[j] = i;
            //         DCB->FreqMap[i] = j;
            //         ++j;
            //      }
            //      else DCB->FreqMap[i] = -1;
            //   }
            for (i = 0, j = 0; i < DBASE_MAXITEM; ++i) {
                if (itcnt[i] >= MINSUPPORT) {
                    if (Paramenters.output && Paramenters.alg_type == alg_vals.eclat) {
                        //outfile << i << " - " << itcnt[i] << endl;
                    }
                    DCB.FreqIdx[j] = i;
                    DCB.FreqMap[i] = j;
                    ++j;
                } else DCB.FreqMap[i] = -1;
            }

            //   //cout<< "F1 - " << DCB->NumF1 << " " << DBASE_MAXITEM << endl;  
            DCB.AllocParentClass(itcnt);
            //   DCB->alloc_ParentClass(itcnt);

            //   te = tt.Stop();
            //   stats.add(DBASE_MAXITEM, DCB->NumF1, te);

            //}
        }

    }
}